package org.jsola.hr.service.excel;

import com.alibaba.excel.context.AnalysisContext;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.lang3.StringUtils;
import org.jsola.common.BeanKit;
import org.jsola.common.DateKit8;
import org.jsola.exception.ParamException;
import org.jsola.hr.common.DateUtils;
import org.jsola.hr.constant.EmpSocStatus;
import org.jsola.hr.dto.ImportIncreaseDTO;
import org.jsola.hr.entity.*;
import org.jsola.hr.service.*;
import org.jsola.user.core.TokenUser;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;


/**
 * @author zhr
 */
@Slf4j
@Data
@EqualsAndHashCode(callSuper = true)
public class ImportIncreaseListener extends BaseAnalysisEventListener<Map<Integer, String>> {

    /**
     * 表头
     */
    private Map<Integer, String> allHeadMap;

    /**
     * 表头
     */
    private List<String> headList = new ArrayList<>();

    /**
     * 表头
     */
    private Map<Integer, String> importBeanAttributeMap = new HashMap<>(8);

    /**
     * List最大盛装数据条数
     */
    private static final Integer MAX_COUNT = 1000;

    /**
     * <身份证号, 对应的实体类>
     */
    private Map<String, ImportIncreaseDTO> importEmpMap = new HashMap<>(256);

    /**
     * 增员类型的集合(校验错误用)
     */
    private List<String> increaseNameList = new ArrayList<>();

    /**
     * 公司Id
     */
    private String companyId;

    /**
     * 入库数据
     */
    private List<ImportIncreaseDTO> saveDataList = new ArrayList<>();

    /**
     * 错误数据
     */
    private List<ImportIncreaseDTO> errorList = new ArrayList<>();

    /**
     * 公司下所有方案名称
     */
    private List<String> schemeNameList = new ArrayList<>();

    /**
     * <方案名称, 对应可以增员哪种类型>
     */
    private Map<String, List<String>> schemeTypeMap = new HashMap<>(32);

    /**
     * <方案名称, 方案子项>
     */
    private Map<String, List<SocProgItemDO>> schemeItemMap = new HashMap<>(32);

    /**
     * 员工对应的增员类型
     */
    private Map<String, List<String>> empIncreaseTypeMap = new HashMap<>(256);

    /**
     * 表头前六列固定不变
     */
    private static final Integer SIX = 6;

    /**
     * 5
     */
    private static final Integer FIVE = 5;

    /**
     * 逗号
     */
    private static final String COMMA = ",";

    /**
     * 点
     */
    private static final String POINT = ".";

    /**
     * 社保增员
     */
    private static final String SOC_INCREASE = "社保增员";

    /**
     * 医保增员
     */
    private static final String ME_INCREASE = "医保增员";

    /**
     * 公积金增员
     */
    private static final String PF_INCREASE = "公积金增员";

    /**
     * 社保子项
     */
    private static final Integer SOC_ITEM = 1;

    /**
     * 医保子项
     */
    private static final Integer ME_ITEM = 3;

    /**
     * 公积金子项
     */
    private static final Integer PF_ITEM = 2;

    /**
     * Excel导入数据条目
     */
    private List<Map<Integer, String>> dataList = new ArrayList<>();

    @Override
    public void invoke(Map<Integer, String> data, AnalysisContext context) {
        // 当前行号
        int row = context.readRowHolder().getRowIndex();
        if(!ObjectUtils.isEmpty(data.get(0)) && "示例员工张三".equals(data.get(0))){
            return;
        }
        dataList.add(data);
        // 传进来的data是 <列数,当列内容>  这种形式,把他转变一下变成 <列名,列对应的值> 这种形式
        Map<String, String> conversionData = new HashMap<>(16);
        for (Integer columnIndex : allHeadMap.keySet()) {
            conversionData.put(allHeadMap.get(columnIndex),
                    StringUtils.isEmpty(data.get(columnIndex)) ? "--" : data.get(columnIndex));
        }
        checkError(data, conversionData);
        if (saveDataList.size() >= MAX_COUNT) {
            // 入库
            saveData();
            // 存储完成清理 list
            saveDataList.clear();
        }
    }

    /**
     * 入库
     */
    @Override
    public void saveData() {
        log.info("{}条数据，开始存储数据库！", saveDataList.size());
        if (CollectionUtils.isEmpty(saveDataList) && CollectionUtils.isEmpty(errorList)) {
            throw new ParamException("导入数据为空");
        }
        IEmpSocPlanService empSocPlanService = BeanKit.getBean(IEmpSocPlanService.class);
        empSocPlanService.importIncreaseSaveData(saveDataList, companyId, tokenUser);
    }

    @Override
    public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
        allHeadMap = headMap;
        List<String> checkHeadList = new ArrayList<>();
        List<String> repeatHead = new ArrayList<>();
        checkHeadList.add("姓名");
        checkHeadList.add("证件号码");
        checkHeadList.add("参保月份");
        checkHeadList.add("参保方案名称");
        checkHeadList.add("服务费");
        checkHeadList.add("增员类型");
        for (Integer headRow : headMap.keySet()) {
            // 前六列固定不变 只需要校验一下是否和自己定义的表头是否相同就行了
            String importHead = headMap.get(headRow);
            if (headRow < SIX) {
                String checkHead = checkHeadList.get(headRow);
                if (!checkHead.equals(importHead)) {
                    throw new ParamException("表头第" + (headRow + 1) + "列应为" + checkHead);
                }
            } else {
                // 从第六列往后是 ***基数  需要校验 是否有重复
                if (repeatHead.contains(importHead)) {
                    throw new ParamException("表头" + importHead + "重复");
                }
                if (!importHead.contains("基数")) {
                    throw new ParamException("表头中子项名称必须以[基数]结尾");
                }
                repeatHead.add(importHead);
            }
            headList.add(importHead);
        }
        initScheme(companyId, tokenUser);
        importBeanAttributeMap.put(0, "name");
        importBeanAttributeMap.put(1, "idCard");
        importBeanAttributeMap.put(2, "socDate");
        importBeanAttributeMap.put(3, "schemeName");
        importBeanAttributeMap.put(4, "serviceCost");
        importBeanAttributeMap.put(5, "increaseType");
        increaseNameList.add("社保增员");
        increaseNameList.add("医保增员");
        increaseNameList.add("公积金增员");
    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        // 这里也要保存数据，确保最后遗留的数据也存储到数据库
        if (!CollectionUtils.isEmpty(saveDataList)) {
            saveData();
        }
    }

    /**
     * 校验数据错误
     */
    private void checkError(Map<Integer, String> data, Map<String, String> conversionData) {
        IEmpInfoService empInfoService = BeanKit.getBean(IEmpInfoService.class);
        IEmpSocRelatedService empSocRelatedService = BeanKit.getBean(IEmpSocRelatedService.class);
        IEmpSocPlanService empSocPlanService = BeanKit.getBean(IEmpSocPlanService.class);
        boolean haveError = false;
        // 先把 data 封装成一个对象   要不不好校验错误
        Map<String, String> conversion = new HashMap<>(16);
        Map<String, String> itemMap = new HashMap<>(8);
        // 这里为什么没有用  invoke 方法里面的 conversionData集合  因为这个集合不好控制前六列
        for (Integer columnIndex : data.keySet()) {
            // 前六列是固定不变的属性直接往Map里面放 可以用工具类直接把 Map 转为JavaBean
            String value = data.get(columnIndex);
            if (columnIndex < SIX) {
                String attributeName = importBeanAttributeMap.get(columnIndex);
                conversion.put(attributeName, value);
            } else {
                // 第六列往后是动态的 并且他们是一个合集  把他们放到一个 Map<String, String>里
                if (!ObjectUtils.isEmpty(value) && value.contains(",")) {
                    value = value.replace(",", "");
                }
                itemMap.put(allHeadMap.get(columnIndex), value);
            }
        }
        ImportIncreaseDTO importIncreaseDTO = new ImportIncreaseDTO();
        importIncreaseDTO.setExcelDate(conversionData);
        try {
            BeanUtils.populate(importIncreaseDTO, conversion);
        } catch (Exception e) {
            throw new ParamException("模板错误");
        }
        importEmpMap.put(importIncreaseDTO.getIdCard(), importIncreaseDTO);
        // 开始校验错误
        Map<String, String> errorInfo = new HashMap<>(16);
        String name = importIncreaseDTO.getName();
        // 如果姓名为空  后面的直接不用做校验了
        if (StringUtils.isEmpty(name)) {
            haveError = true;
            errorInfo.put("姓名", "姓名不得为空;");
        } else {
            String idCard = importIncreaseDTO.getIdCard();
            // 如果证件号码为空  后面的直接不用做校验了
            if (StringUtils.isEmpty(idCard)) {
                haveError = true;
                errorInfo.put("证件号码", "证件号码不得为空;");
            } else {
                EmpInfoDO empInfoDO = empInfoService.selectIdCardAndPhone(idCard, name, companyId, tokenUser);
                // 校验员工是否存在
                if (ObjectUtils.isEmpty(empInfoDO)) {
                    haveError = true;
                    errorInfo.put("姓名", "员工无法匹配;");
                } else {
                    EmpSocRelatedDO empSocRelatedDO =
                            empSocRelatedService.selectByEmpId(empInfoDO.getId(), tokenUser.getSiteId());
                    importIncreaseDTO.setEmpId(empInfoDO.getId());
                    // 校验参保月分
                    String socDate = importIncreaseDTO.getSocDate();
                    if (ObjectUtils.isEmpty(socDate)) {
                        haveError = true;
                        errorInfo.put("参保月份", "参保月份不得为空;");
                    } else {
                        if (checkDateFormat(socDate)) {
                            haveError = true;
                            errorInfo.put("参保月份", "参保月份格式错误;");
                        } else if (checkDateGreaterThanCurrent(socDate)) {
                            haveError = true;
                            errorInfo.put("参保月份", "参保月份时间不得晚于系统本月时间;");
                        }
                    }
                    // 校验服务费
                    String serviceCost = importIncreaseDTO.getServiceCost();
                    if (StringUtils.isEmpty(serviceCost)) {
                        importIncreaseDTO.setServiceCost("0");
                    } else {
                        if (serviceCost.contains(COMMA)) {
                            serviceCost = serviceCost.replace(",", "");
                            importIncreaseDTO.setServiceCost(serviceCost);
                        }
                        if (checkAmount(serviceCost)) {
                            haveError = true;
                            errorInfo.put("服务费", "服务费格式错误;");
                        } else {
                            if (serviceCost.length() > FIVE) {
                                haveError = true;
                                errorInfo.put("服务费", "服务费长度超过限制;");
                            }
                        }
                    }
                    // 校验方案以及里面的基数
                    String schemeName = importIncreaseDTO.getSchemeName();
                    if (StringUtils.isEmpty(schemeName)) {
                        haveError = true;
                        errorInfo.put("参保方案名称", "参保方案不得为空;");
                    } else if (!schemeNameList.contains(schemeName)) {
                        haveError = true;
                        errorInfo.put("参保方案名称", "参保方案无法匹配;");
                    } else {
                        // 校验增员类型
                        String increaseType = importIncreaseDTO.getIncreaseType();
                        if (StringUtils.isEmpty(increaseType)) {
                            haveError = true;
                            errorInfo.put("增员类型", "增员类型不得为空;");
                        } else {
                            List<String> empIncreaseType = empIncreaseTypeMap.get(idCard);
                            List<String> typeList = new ArrayList<>(Arrays.
                                    asList(StringUtils.split(increaseType, ";；")));
                            importIncreaseDTO.setTypeList(typeList);
                            if (checkRepeatIncrease(idCard, empIncreaseType, typeList)) {
                                haveError = true;
                                errorInfo.put("增员类型", "增员类型重复;");
                            } else if (checkIncreaseTypeFormat(typeList)) {
                                haveError = true;
                                errorInfo.put("增员类型", "增员类型名称错误;");
                            } else {
                                List<Integer> checkStatusList = Arrays.asList(EmpSocStatus.NORMAL_SOC.getValue(),
                                        EmpSocStatus.INCREMENT.getValue());
                                // 如果增员类型没写错  就再校验一下方案与增员类型是否冲突
                                List<String> canTypeList = schemeTypeMap.get(schemeName);
                                for (String inputType : typeList) {
                                    if (!canTypeList.contains(inputType)) {
                                        haveError = true;
                                        errorInfo.put("增员类型", "增员类型与方案不匹配;");
                                    }
                                }
                                List<SocProgItemDO> itemList = schemeItemMap.get(schemeName);
                                List<String> canItemList = new ArrayList<>();
                                if (typeList.contains(SOC_INCREASE)) {
                                    canItemList.addAll(itemList.stream().filter(item -> item.getItemType() == 1)
                                            .map(SocProgItemDO::getItemName).collect(Collectors.toList()));
                                    if (checkStatusList.contains(empSocRelatedDO.getSocState())) {
                                        haveError = true;
                                        errorInfo.put("增员类型", "员工社保已经是增员或正常参保状态");
                                    } else {
                                        List<EmpSocPlanDO> empSocPlanList = empSocPlanService
                                                .selectPlanByEmpId(empInfoDO.getId(), SOC_ITEM, tokenUser);
                                        if (!CollectionUtils.isEmpty(empSocPlanList)) {
                                            haveError = true;
                                            errorInfo.put("增员类型", "该员工已存在社保增员计划;");
                                        }
                                    }
                                }
                                if (typeList.contains(ME_INCREASE)) {
                                    canItemList.addAll(itemList.stream().filter(item -> item.getItemType() == 3)
                                            .map(SocProgItemDO::getItemName).collect(Collectors.toList()));
                                    if (checkStatusList.contains(empSocRelatedDO.getMeState())) {
                                        haveError = true;
                                        errorInfo.put("增员类型", "员工医保已经是增员或正常参保状态");
                                    } else {
                                        List<EmpSocPlanDO> empSocPlanList = empSocPlanService
                                                .selectPlanByEmpId(empInfoDO.getId(), ME_ITEM, tokenUser);
                                        if (!CollectionUtils.isEmpty(empSocPlanList)) {
                                            haveError = true;
                                            errorInfo.put("增员类型", "该员工已存在医保增员计划;");
                                        }
                                    }
                                }
                                if (typeList.contains(PF_INCREASE)) {
                                    canItemList.addAll(itemList.stream().filter(item -> item.getItemType() == 2)
                                            .map(SocProgItemDO::getItemName).collect(Collectors.toList()));
                                    if (checkStatusList.contains(empSocRelatedDO.getPfState())) {
                                        haveError = true;
                                        errorInfo.put("增员类型", "员工公积金已经是增员或正常参保状态");
                                    } else {
                                        List<EmpSocPlanDO> empSocPlanList = empSocPlanService
                                                .selectPlanByEmpId(empInfoDO.getId(), PF_ITEM, tokenUser);
                                        if (!CollectionUtils.isEmpty(empSocPlanList)) {
                                            haveError = true;
                                            errorInfo.put("增员类型", "该员工已存在公积金增员计划;");
                                        }
                                    }
                                }
                                for (String itemName : itemMap.keySet()) {
                                    String itemCardinality = itemMap.get(itemName);
                                    if (!StringUtils.isEmpty(itemCardinality)) {
                                        if (itemCardinality.contains(",")) {
                                            itemCardinality = itemCardinality.replace(",", "");
                                        }
                                        if (checkAmount(itemCardinality)) {
                                            haveError = true;
                                            errorInfo.put(itemName, "基数格式错误;");
                                        } else {
                                            if(!StringUtils.isEmpty(checkCardinalityLength(itemCardinality))){
                                                haveError = true;
                                                errorInfo.put(itemName, checkCardinalityLength(itemCardinality));
                                            } else {
                                                String subItemName = itemName.substring(0, itemName.indexOf("基数"));
                                                if (!canItemList.contains(subItemName)) {
                                                    haveError = true;
                                                    errorInfo.put(itemName, "基数无法匹配;");
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        if (!haveError) {
            importIncreaseDTO.setItemMap(itemMap);
            saveDataList.add(importIncreaseDTO);
        } else {
            importIncreaseDTO.setErrorInfo(errorInfo);
            errorList.add(importIncreaseDTO);
        }
    }

    /**
     * true 格式错误
     */
    private Boolean checkDateFormat(String date) {
        String replaceData = date.replace("/", "-");
        String replaceDataAdd = replaceData + "-01";
        try {
            DateKit8.parseDate(replaceDataAdd);
        } catch (Exception e) {
            return true;
        }
        return false;
    }

    /**
     * 判断金额长度
     */
    private String checkCardinalityLength(String cardinality) {
        StringBuilder lengthError = new StringBuilder();
        if (cardinality.contains(POINT)) {
            String[] split = StringUtils.split(cardinality,".");
            cardinality = split[0];
            if (split[1].length() > 4) {
                lengthError.append("小数点后最多4位小数；");
            }
        }
        if (cardinality.length() > 7) {
            lengthError.append("小数点前最多7位");
        }
        return lengthError.toString();
    }

    /**
     * 判断excel键入的时间是否小于当前时间
     */
    private Boolean checkDateGreaterThanCurrent(String date) {
        String replaceDate = date.replace("/", "");
        // excel键入的时间
        int scannerDate = Integer.parseInt(replaceDate);
        // 当前时间
        int currentDate = Integer.parseInt(DateUtils.getYearMonthStr(new Date()));
        return scannerDate < currentDate;
    }

    /**
     * 校验服务费格式
     */
    private Boolean checkAmount(String amountStr) {
        try {
            BigDecimal bigDecimal = new BigDecimal(amountStr);
            return bigDecimal.compareTo(BigDecimal.ZERO) < 0;
        } catch (Exception e) {
            return true;
        }
    }

    /**
     * 校验他的增员类型写的字儿对不对
     */
    private Boolean checkIncreaseTypeFormat(List<String> typeList) {

        for (String type : typeList) {
            if (!increaseNameList.contains(type)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 校验是否重复增员
     */
    private Boolean checkRepeatIncrease(String idCard, List<String> empIncreaseType, List<String> typeList) {
        if (CollectionUtils.isEmpty(empIncreaseType)) {
            empIncreaseType = new ArrayList<>();
        }
        for (String type : typeList) {
            if (empIncreaseType.contains(type)) {
                return true;
            }
            empIncreaseType.add(type);
        }
        empIncreaseTypeMap.put(idCard, empIncreaseType);
        return false;
    }

    private void initScheme(String companyId, TokenUser tokenUser) {
        Map<Integer, String> itemTypeMap = new HashMap<>(8);
        itemTypeMap.put(1, "社保增员");
        itemTypeMap.put(2, "公积金增员");
        itemTypeMap.put(3, "医保增员");
        ISocProgService socProgService = BeanKit.getBean(ISocProgService.class);
        ISocProgItemService socProgItemService = BeanKit.getBean(ISocProgItemService.class);
        List<SocProgDO> socProgList = socProgService.selectSocProgByCompanyId(companyId, tokenUser);
        if (!CollectionUtils.isEmpty(socProgList)) {
            schemeNameList = socProgList.stream().map(SocProgDO::getProgName).collect(Collectors.toList());
            for (SocProgDO socProgDO : socProgList) {
                List<SocProgItemDO> socProgItemList = socProgItemService
                        .selectBySocProgId(socProgDO.getId(), tokenUser.getSiteId());
                schemeItemMap.put(socProgDO.getProgName(), socProgItemList);
                List<Integer> typeList = socProgItemList.stream()
                        .map(SocProgItemDO::getItemType).distinct().collect(Collectors.toList());
                List<String> increaseTypeList = new ArrayList<>();
                for (Integer integer : typeList) {
                    String type = itemTypeMap.get(integer);
                    if (!StringUtils.isEmpty(type)) {
                        increaseTypeList.add(type);
                    }
                }
                schemeTypeMap.put(socProgDO.getProgName(), increaseTypeList);
            }
        }
    }
}



